/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package server;

import chat.ChatFrame;
import compute.Compute;
import compute.Gridify;
import compute.GridifyPrime;
import compute.Task;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author spider391Tang
 */
public class ChatServer implements Compute {

    public static void main(String[] args) throws IOException {

        ServerSocket serverSocket = null;
        boolean listening = true;
        PrintWriter connect_log_out = new PrintWriter(new FileWriter("connect_log.txt"));
        connect_log_out.println("IP/port	       logID");
        int connect_id = 1;

        try {
            serverSocket = new ServerSocket(7011);
        } catch (IOException e) {
            System.err.println("Could not listen on port: 7010.");
            System.exit(1);
        }
        int count = 1;
        reg_to_rmi("@SERVER");
        System.out.println("[Chat Server Start]");
        while (listening) {

            Socket s = serverSocket.accept();
            System.out.println(s.getInetAddress().getHostAddress() + "/" + s.getPort() + "	" + connect_id);
            connect_log_out.println(s.getInetAddress().getHostAddress() + "/" + s.getPort() + "	" + connect_id);
            connect_log_out.flush();

            new KKMultiServerThread(s, connect_id).start();
            connect_id++;
        }


        connect_log_out.close();
        serverSocket.close();

    }

    class TaskThread extends java.lang.Thread {

        Task t;
        String name;
        private final int index;
        private final Vector v;

        TaskThread(String name, Task t, Vector v, int index) {
            this.name = name;
            this.t = t;
            this.v = v;
            this.index = index;
        }

        public void run() {
            Registry registry;
            Compute comp = null;
            try {
                registry = LocateRegistry.getRegistry("localhost");
                comp = (Compute) registry.lookup(name);
                //System.out.println("[run]: "+ comp);
                //System.out.println("[task]:" + t);
                //comp.executeTask(t, "");
               // v.insertElementAt(comp.executeTask(t, null),index );
               // v.add(index, comp.executeTask(t, null));
                //v.elementAt(index) = (Object) comp.executeTask(t, null);
               // System.out.println("("+((GridifyPrime)t).prime+","+((GridifyPrime)t).min+","+((GridifyPrime)t).max+")");
                v.set(index, comp.executeTask(t, null));
                //v.insertElementAt(comp, index);
            } catch (NotBoundException ex) {
                Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
            } catch (AccessException ex) {
                Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
            } catch (RemoteException ex) {
                Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public <T> T executeTask(Task<T> t, String target) throws RemoteException {


        //t.init(target);

        Class c = t.getClass();
        System.out.println("[Server] executeTask (" + t +","+ target + ")");
        t.init(target);

        try {
            Method method = c.getMethod("execute");
            if (!method.isAnnotationPresent(Gridify.class)) {

                return t.execute();
            } else {
                System.out.println("@Gridify is found.");
                Gridify g = method.getAnnotation(Gridify.class);
                System.out.println(g.mapper());
                System.out.println(g.reducer());
                Class mapper_types[] = new Class[1];
                mapper_types[0] = int.class;
                Class reducer_types[] = new Class[1];
                reducer_types[0] = Vector.class;
                Method mapper = c.getMethod(g.mapper(), mapper_types);
                Method reducer = c.getMethod(g.reducer(), reducer_types);

                if (mapper == null) {
                    System.out.println("Mapper not found");
                    return t.execute();
                }

                int user_num = KKMultiServerThread.getChatMap().size();
                Object mapper_args[] = new Object[1];
                mapper_args[0] = new Integer(user_num);

                TaskThread task_thread[] = new TaskThread[user_num];
                Vector<?> ret_mapper = null;
                try {
                    ret_mapper = (Vector<?>) mapper.invoke(t, mapper_args);

                    Vector result_vec = new Vector(user_num);
                    //System.out.println("vector size = " + result_vec.size());
                    for(int i=0;i<user_num;i++){
                        result_vec.add(i);
                    }


                    Set set = KKMultiServerThread.getChatMap().entrySet();
                    Iterator It = set.iterator();
                    int index = 0;
                    while (It.hasNext()) {
                        Map.Entry me = (Map.Entry) It.next();
                        System.out.println("[dispatch task] ==> " + "(" + me.getKey() + "," + t + ")");
                        task_thread[index] = new TaskThread((String) me.getKey(), (Task) ret_mapper.elementAt(index), result_vec, index);
                        task_thread[index].start();
                        index++;
                    }
                    System.out.println("join....");

                    for(int i=0;i<user_num;i++){
                        try {
                            task_thread[i].join();
                        } catch (InterruptedException ex) {
                            Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                    
                    Object reducer_args[] = new Object[1];
                    reducer_args[0] =result_vec;
                    return (T) reducer.invoke(t, reducer_args);

                } catch (IllegalAccessException ex) {
                    Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IllegalArgumentException ex) {
                    Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
                } catch (InvocationTargetException ex) {
                    Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
                }

            }

        } catch (NoSuchMethodException ex) {
            Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
        }

        //        Method mm[];
        //        mm = c.getDeclaredMethods();
        //        for (int i = 0; i < mm.length; i++) {
        //            int md = mm[i].getModifiers();
        //            System.out.println("    " + Modifier.toString(md) + " " + mm[i].getReturnType().getName() + " " + mm[i].getName());
        //            //Method method = c.getMethod("execute");
        //            if (mm[i].isAnnotationPresent(Gridify.class)) {
        //                System.out.println("@Gridify is found.");
        //            }
        //        }


//        Method mm[];
//        mm = c.getDeclaredMethods();
//        for (int i = 0; i < mm.length; i++) {
//            int md = mm[i].getModifiers();
//            System.out.println("    " + Modifier.toString(md) + " " + mm[i].getReturnType().getName() + " " + mm[i].getName());
//            Class cx[] = mm[i].getParameterTypes();
//            System.out.print("(");
//            for(int j=0;j<cx.length;j++){
//                System.out.println(cx[j].getName());
//                if(j<(cx.length-1)) System.out.print(", ");
//            }
//            System.out.println(")");
//        }
        //            //Method method = c.getMethod("execute");
        //            if (mm[i].isAnnotationPresent(Gridify.class)) {
        //                System.out.println("@Gridify is found.");
        //            }
        //        }

        return t.execute();
    }

    private static void reg_to_rmi(String name) {
        /// 公子乖 -　向 rmiregistry 註冊，使用預設 port 1099
        // 	Each client uses his name for rebind().
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
        }
        Compute engine = new ChatServer();
        Compute stub;
        try {
            stub = (Compute) UnicastRemoteObject.exportObject(engine, 0);
            Registry registry = LocateRegistry.getRegistry();
            registry.rebind(name, stub);
            System.out.println(name + " bound to RMIRegisty");
        } catch (RemoteException ex) {
            Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
